/**
* @file kernel.cpp
*
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All Rights reserved.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include "runtime/acl_rt_impl.h"
#include "runtime/kernel.h"
#include "runtime/rts/rts_kernel.h"
#include "log_inner.h"
#include "error_codes_inner.h"
#include "toolchain/resource_statistics.h"

aclrtBinary aclrtCreateBinaryImpl(const void *data, size_t dataLen)
{
  ACL_ADD_APPLY_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_ALLOCATOR_BINARY_DESC);
  ACL_LOG_INFO("start to execute aclrtCreateBinary");
  ACL_REQUIRES_NOT_NULL_RET_NULL_INPUT_REPORT(data);

  rtDevBinary_t *binaryDesc = new(std::nothrow) rtDevBinary_t();
  ACL_CHECK_WITH_MESSAGE_AND_RETURN(binaryDesc != nullptr, nullptr, "Allocate memory for binaryDesc failed.");

  binaryDesc->magic = 0U;
  binaryDesc->version = 0U;
  binaryDesc->data = data;
  binaryDesc->length = static_cast<uint64_t>(dataLen);

  ACL_ADD_APPLY_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_ALLOCATOR_BINARY_DESC);
  return binaryDesc;
}

aclError aclrtDestroyBinaryImpl(aclrtBinary binary)
{
  ACL_ADD_RELEASE_TOTAL_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_ALLOCATOR_BINARY_DESC);
  ACL_LOG_INFO("start to execute aclrtDestroyBinary");
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(binary);

  delete static_cast<rtDevBinary_t *>(binary);

  ACL_ADD_RELEASE_SUCCESS_COUNT(acl::ACL_STATISTICS_CREATE_DESTROY_ALLOCATOR_BINARY_DESC);
  return ACL_SUCCESS;
}

aclError aclrtBinaryLoadImpl(const aclrtBinary binary, aclrtBinHandle *binHandle)
{
  ACL_ADD_APPLY_TOTAL_COUNT(acl::ACL_STATISTICS_LOAD_UNLOAD_BINARY);
  ACL_LOG_INFO("start to execute aclrtBinaryLoad");
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(binary);
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(binHandle);
  rtDevBinary_t *bin = static_cast<rtDevBinary_t *>(binary);
  const rtError_t rtErr = rtBinaryLoadWithoutTilingKey(bin->data, bin->length, binHandle);
  if (rtErr != RT_ERROR_NONE) {
    ACL_LOG_CALL_ERROR("rtBinaryLoad failed, runtime result = %d.", rtErr);
    return ACL_GET_ERRCODE_RTS(rtErr);
  }

  ACL_ADD_APPLY_SUCCESS_COUNT(acl::ACL_STATISTICS_LOAD_UNLOAD_BINARY);
  return ACL_SUCCESS;
}

aclError aclrtBinaryUnLoadImpl(aclrtBinHandle binHandle)
{
  ACL_ADD_RELEASE_TOTAL_COUNT(acl::ACL_STATISTICS_LOAD_UNLOAD_BINARY);
  ACL_LOG_INFO("start to execute aclrtBinaryUnLoad");
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(binHandle);

  const rtError_t rtErr = rtBinaryUnLoad(binHandle);
  if (rtErr != RT_ERROR_NONE) {
    ACL_LOG_CALL_ERROR("rtBinaryUnLoad failed, runtime result = %d.", rtErr);
    return ACL_GET_ERRCODE_RTS(rtErr);
  }

  ACL_ADD_RELEASE_SUCCESS_COUNT(acl::ACL_STATISTICS_LOAD_UNLOAD_BINARY);
  return ACL_SUCCESS;
}

aclError aclrtBinaryGetFunctionImpl(const aclrtBinHandle binHandle, const char *kernelName, aclrtFuncHandle *funcHandle)
{
  ACL_LOG_INFO("start to execute aclrtBinaryGetFunction");
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(binHandle);
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(kernelName);
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(funcHandle);

  // currently not support multi kernel, so tilingKey always use 0
  const rtError_t rtErr = rtsFuncGetByName(binHandle, kernelName, funcHandle);
  if (rtErr != RT_ERROR_NONE) {
    ACL_LOG_CALL_ERROR("rtBinaryGetFunction failed, runtime result = %d.", rtErr);
    return ACL_GET_ERRCODE_RTS(rtErr);
  }

  return ACL_SUCCESS;
}

aclError aclrtLaunchKernelImpl(aclrtFuncHandle funcHandle, uint32_t blockDim, const void *argsData,
                               size_t argsSize, aclrtStream stream)
{
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(funcHandle);
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(argsData);

  rtArgsEx_t argsInfo = {};
  argsInfo.args = const_cast<void *>(argsData);
  argsInfo.argsSize = static_cast<uint32_t>(argsSize);
  argsInfo.isNoNeedH2DCopy = 1U;

  const rtError_t rtErr = rtLaunchKernelByFuncHandleV3(funcHandle, blockDim, &argsInfo, stream, nullptr);
  if (rtErr != RT_ERROR_NONE) {
    ACL_LOG_CALL_ERROR("rtLaunchKernelByFuncHandleV3 failed, runtime result = %d.", rtErr);
    return ACL_GET_ERRCODE_RTS(rtErr);
  }

  return ACL_SUCCESS;
}

aclError aclrtBinaryLoadFromFileImpl(const char* binPath, aclrtBinaryLoadOptions *options, aclrtBinHandle *binHandle)
{
    ACL_LOG_INFO("start to execute aclrtBinaryLoadFromFile, binPath[%s]", binPath);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(binPath);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(binHandle);
    const rtLoadBinaryConfig_t *rt_options = nullptr;
    if (options != nullptr) {
       rt_options = reinterpret_cast<rtLoadBinaryConfig_t *>(options);
    }
    const auto rtErr = rtsBinaryLoadFromFile(binPath, rt_options, binHandle);
    if (rtErr != RT_ERROR_NONE) {
       ACL_LOG_CALL_ERROR("Binary load from file Failed, runtime result = %d", rtErr);
       return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtBinaryGetFunctionByEntryImpl(aclrtBinHandle binHandle, uint64_t funcEntry, aclrtFuncHandle *funcHandle)
{
    ACL_LOG_INFO("start to execute aclrtBinaryGetFunctionByEntry");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(binHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(funcHandle);

    const auto rtErr = rtsFuncGetByEntry(binHandle, funcEntry, funcHandle);
    if (rtErr != RT_ERROR_NONE) {
       ACL_LOG_CALL_ERROR("Binary get function by entry Failed, runtime result = %d", rtErr);
       return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtGetFunctionAddrImpl(aclrtFuncHandle funcHandle, void **aicAddr, void **aivAddr)
{
    ACL_LOG_INFO("start to execute aclrtGetFunctionAddr");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(funcHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(aicAddr);
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(aivAddr);

    const auto rtErr = rtsFuncGetAddr(funcHandle, aicAddr, aivAddr);
    if (rtErr != RT_ERROR_NONE) {
       ACL_LOG_CALL_ERROR("Get function addr Failed, runtime result = %d", rtErr);
       return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtLaunchKernelWithConfigImpl(aclrtFuncHandle funcHandle, uint32_t blockDim, aclrtStream stream,
                                         aclrtLaunchKernelCfg *cfg, aclrtArgsHandle argsHandle, void *reserve)
{
    ACL_LOG_INFO("Start to execute aclrtLaunchKernelWithConfig");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(funcHandle);
    ACL_REQUIRES_POSITIVE_WITH_INPUT_REPORT(blockDim);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(stream);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(argsHandle);
    if (reserve != nullptr) {
        ACL_LOG_ERROR("[Check][reserve]param is reserved and only support currently nullptr."); \
        return ACL_ERROR_INVALID_PARAM;
    }

    rtKernelLaunchCfg_t *rt_cfg = nullptr;
    if (cfg != nullptr ) {
        rt_cfg = reinterpret_cast<rtKernelLaunchCfg_t *>(cfg);
    }
    const auto rtErr = rtsLaunchKernelWithConfig(funcHandle, blockDim, stream, rt_cfg, argsHandle, reserve);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Launch kernel with config Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsInitImpl(aclrtFuncHandle funcHandle, aclrtArgsHandle *argsHandle)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsInit");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(funcHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(argsHandle);

    const auto rtErr = rtsKernelArgsInit(funcHandle, argsHandle);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Initialize kernel args Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsInitByUserMemImpl(aclrtFuncHandle funcHandle, aclrtArgsHandle argsHandle, void *userHostMem,
                                          size_t actualArgsSize)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsInitByUserMem");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(funcHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(argsHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(userHostMem);
    ACL_REQUIRES_POSITIVE_WITH_INPUT_REPORT(actualArgsSize);

    const auto rtErr = rtsKernelArgsInitByUserMem(funcHandle, argsHandle, userHostMem, actualArgsSize);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Initialize kernel args by user mem Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsGetMemSizeImpl(aclrtFuncHandle funcHandle, size_t userArgsSize, size_t *actualArgsSize)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsGetMemSize");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(funcHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(actualArgsSize);

    const auto rtErr = rtsKernelArgsGetMemSize(funcHandle, userArgsSize, actualArgsSize);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Get kernel args mem size Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsGetHandleMemSizeImpl(aclrtFuncHandle funcHandle, size_t *memSize)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsGetHandleMemSize");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(funcHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(memSize);

    const auto rtErr = rtsKernelArgsGetHandleMemSize(funcHandle, memSize);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Get kernel args handle mem size Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsAppendImpl(aclrtArgsHandle argsHandle, void *param, size_t paramSize,
                                   aclrtParamHandle *paramHandle)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsAppend");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(argsHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(param);
    ACL_REQUIRES_POSITIVE_WITH_INPUT_REPORT(paramSize);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(paramHandle);

    const auto rtErr = rtsKernelArgsAppend(argsHandle, param, paramSize, paramHandle);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Append kernel args Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsAppendPlaceHolderImpl(aclrtArgsHandle argsHandle, aclrtParamHandle *paramHandle)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsAppendPlaceHolder");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(argsHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(paramHandle);

    const auto rtErr = rtsKernelArgsAppendPlaceHolder(argsHandle, paramHandle);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Append kernel args placeholder Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsGetPlaceHolderBufferImpl(aclrtArgsHandle argsHandle, aclrtParamHandle paramHandle,
                                                 size_t dataSize, void **bufferAddr)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsGetPlaceHolderBuffer");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(argsHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(paramHandle);
    ACL_REQUIRES_POSITIVE_WITH_INPUT_REPORT(dataSize);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(bufferAddr);

    const auto rtErr = rtsKernelArgsGetPlaceHolderBuffer(argsHandle, paramHandle, dataSize, bufferAddr);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Get kernel args placeholder buffer Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsParaUpdateImpl(aclrtArgsHandle argsHandle, aclrtParamHandle paramHandle, void *param,
                                       size_t paramSize)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsParaUpdate");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(argsHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(paramHandle);
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(param);
    ACL_REQUIRES_POSITIVE_WITH_INPUT_REPORT(paramSize);

    const auto rtErr = rtsKernelArgsParaUpdate(argsHandle, paramHandle, param, paramSize);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Update kernel args placeholder Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtKernelArgsFinalizeImpl(aclrtArgsHandle argsHandle)
{
    ACL_LOG_INFO("Start to execute aclrtKernelArgsFinalize");
    ACL_REQUIRES_NOT_NULL_WITH_INNER_REPORT(argsHandle);

    const auto rtErr = rtsKernelArgsFinalize(argsHandle);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("Finalize kernel args Failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}

aclError aclrtGetThreadLastTaskIdImpl(uint32_t *taskId)
{
    ACL_LOG_DEBUG("start to execute aclrtGetThreadLastTaskId");
    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(taskId);
    const rtError_t rtErr = rtsGetThreadLastTaskId(taskId);
    if (rtErr != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("call rtsGetThreadLastTaskId failed, runtime result = %d", rtErr);
        return ACL_GET_ERRCODE_RTS(rtErr);
    }
    return ACL_SUCCESS;
}
